/*
 *
 * I (David P Shenba) licenses this file to you under the Apache License, 
 * Version 2.0 (the "License"); you may not use this 
 * file except in compliance with the License.  You may 
 * obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.shenba.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * This class is a singleton class for constructing
 * valid HTML string from a given ArrayList table, ul, ol
 * are the supported HTML elements for now
 * @author David P Shenba
 * @version 1.0
 */
public class HTMLBuilder {

	/**
	 * The only constructor
	 */
	private HTMLBuilder() {
		//To avoid instance creation
	}
	
	/**
	 * Singleton instance
	 */
	private static HTMLBuilder instance = null;
	
	/**
	 * This method returns an instance of HTMLBuilder
	 * @return HTMLBuilder instance
	 */
	public static HTMLBuilder getInstance() {
		if(instance == null) {
			instance = new HTMLBuilder();
		}
		return instance;
	}
	
	/**
	 * For a given ArrayList, it returns a HTML table elements
	 * (only rows and columns) along with header.
	 * Header is human readable property names
	 * @param beanList
	 * @param type
	 * @return HTML table rows, columns
	 * @throws IntrospectionException
	 * @throws ClassNotFoundException
	 * @throws NoSuchMethodException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws ClassCastException 
	 */
	public String formattedTable(ArrayList beanList, String type) 
		throws IntrospectionException, ClassNotFoundException, 
		ClassCastException, IllegalAccessException, 
		InvocationTargetException, NoSuchMethodException, 
		NullPointerException {
		
		StringBuffer htmlString = new StringBuffer("\n\t<tr>\n\t\t<th>");
		//if array list is empty
		if(beanList == null || beanList.isEmpty()) {
			htmlString.append("No rows to display</th>\n\t</tr>");
			return htmlString.toString();
		}
		//reading bean property names
		BeanInfo beanInfo = Introspector.getBeanInfo(Class.forName(type));
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		
		htmlString = new StringBuffer(constructTableHeader(pds));
		
		Iterator beanIter = beanList.iterator();
		while(beanIter.hasNext()){
			htmlString.append(constructTableBody(beanIter.next(), pds));
		}
		return htmlString.toString();
	}
	
	/**
	 * Generates a table header from the property descriptor
	 * @param pds
	 * @return
	 * @throws NullPointerException if the property descriptor
	 *                              is null or empty
	 */
	private String constructTableHeader(PropertyDescriptor[] pds) 
		throws NullPointerException{
	
	StringBuffer tableString = new StringBuffer("\n\t<tr>");
	int pdsIndex = 0;
	int pdsSize = pds.length;
	
	if(pds == null || pdsSize == 0) {
		throw new NullPointerException();
	}
	
	for(pdsIndex = 0; pdsIndex < pdsSize; pdsIndex++) {
		if(!"class".equalsIgnoreCase(pds[pdsIndex].getName())) {
			tableString.append("\n\t\t<th>");
			tableString.append(camelCaseToReadable(
					pds[pdsIndex].getName()));
			tableString.append("</th>");
		}
	}
	tableString.append("\n\t</tr>");
	return tableString.toString();
}
	
	/**
	 * @param bean
	 * @param pds
	 * @return
	 * @throws ClassCastException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	private String constructTableBody(Object bean, PropertyDescriptor[] pds) 
		throws ClassCastException, IllegalAccessException, 
		InvocationTargetException, NoSuchMethodException,
		NullPointerException{
		
		StringBuffer tableString = new StringBuffer("\n\t<tr>");
		int pdsIndex = 0;
		int pdsSize = pds.length;
		
		if(bean == null || pds == null) {
			throw new NullPointerException();
		}
		
		for(pdsIndex = 0; pdsIndex < pdsSize; pdsIndex++) {
			if(!"class".equalsIgnoreCase(pds[pdsIndex].getName())) {
				tableString.append("\n\t\t<td>");
				tableString.append(ObjectGrabber.getInstance()
						.getStringFromBean(bean, 
						pds[pdsIndex].getName()));
				tableString.append("</td>");
			}
		}
		tableString.append("\n\t</tr>");
		return tableString.toString();
	}
	
	/**
	 * Parses a camelCase to readable text
	 * Searched in Google but nothing came up :(
	 * This method at least is of some use ;)
	 * @param camelCase
	 * @return readable string
	 * @throws NullPointerException
	 */
	private String camelCaseToReadable(String camelCase) 
		throws NullPointerException{
		
		if(camelCase == null) {
			throw new NullPointerException();
		}
		
		StringBuffer readable = new StringBuffer();
		int strIndex = 0;
		int strLength = camelCase.length();
		
		for(strIndex = 0; strIndex < strLength; strIndex++) {
			if(strIndex == 0) {
				readable.append(camelCase.substring(strIndex, strIndex+1).toUpperCase());
			}
			else if(camelCase.substring(strIndex, strIndex+1)
					.equals(camelCase.substring(strIndex, strIndex+1)
					.toUpperCase())) {
				readable.append(" ");
				readable.append(camelCase.substring(strIndex, strIndex+1).toUpperCase());
			}
			else {
				readable.append(camelCase.substring(strIndex, strIndex+1));
			}
		}
		return readable.toString();
	}
}
